/*
 * @Author: LVGRAPE
 * @Date: 2022-11-12 16:36:32
 * @LastEditTime: 2023-11-15 11:45:10
 * @LastEditors: LVGRAPE
 * @Description:
 * @FilePath: \zino_fc_v0\zino\soft_i2c\soft_i2c.c
 * 可以输入预定的版权声明、个性签名、空行等
 */
#include "soft_i2c.h"
#include "delay.h"
#include "uart.h"
#include "zino.h"

#define DBG_TAG "soft_i2c"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

static void soft_i2c_delay_us(uint32_t us)
{
    delay_us(us);
}
/**
 * @brief send start pulse
 *
 * @param bus
 * @return uint8_t 0--ok!, BIT[3:0]---[sda_h:scl_h:sda_l:scl_l]
 */
static uint8_t zino_soft_i2c_start(zino_soft_i2c_bus_t *bus)
{
    uint8_t ret = 0;
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);

    zino_pin_high(bus->sda);
    zino_pin_high(bus->scl);
    soft_i2c_delay_us(bus->delay_us);

    ret = !zino_pin_read(bus->sda);
    ret <<= 1;
    ret |= !zino_pin_read(bus->scl);
    ret <<= 1;

    zino_pin_low(bus->sda);
    soft_i2c_delay_us(bus->delay_us);
    zino_pin_low(bus->scl);
    soft_i2c_delay_us(bus->delay_us);

    ret = zino_pin_read(bus->sda);
    ret <<= 1;
    ret |= zino_pin_read(bus->scl);
    ret <<= 1;

    return ret;
}
static void zino_soft_i2c_stop(zino_soft_i2c_bus_t *bus)
{
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);

    zino_pin_low(bus->sda);
    soft_i2c_delay_us(bus->delay_us);
    zino_pin_high(bus->scl);
    soft_i2c_delay_us(bus->delay_us);
    zino_pin_high(bus->sda);
    soft_i2c_delay_us(bus->delay_us);
}
static void zino_soft_i2c_sendByte(zino_soft_i2c_bus_t *bus, uint8_t byte)
{
    uint8_t i;
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);
    for (i = 0; i < 8; i++)
    {
        zino_pin_write(bus->sda, byte & 0x80);
        byte <<= 1;
        zino_pin_high(bus->scl);
        soft_i2c_delay_us(bus->delay_us);
        zino_pin_low(bus->scl);
        soft_i2c_delay_us(bus->delay_us);
    }
    zino_pin_high(bus->scl);
}
static uint8_t zino_soft_i2c_readByte(zino_soft_i2c_bus_t *bus)
{
    uint8_t i;
    uint8_t byte = 0;
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_INPUT);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);
    for (i = 0; i < 8; i++)
    {
        byte <<= 1;
        zino_pin_high(bus->scl);
        soft_i2c_delay_us(bus->delay_us);
        byte |= zino_pin_read(bus->sda);
        zino_pin_low(bus->scl);
        soft_i2c_delay_us(bus->delay_us);
    }
    return byte;
}
void zino_soft_i2c_sendAck(zino_soft_i2c_bus_t *bus, uint8_t ack)
{
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);

    zino_pin_write(bus->sda, ack);
    zino_pin_high(bus->scl);
    soft_i2c_delay_us(bus->delay_us);
    zino_pin_low(bus->scl);
    soft_i2c_delay_us(bus->delay_us);
}
static uint8_t zino_soft_i2c_readAck(zino_soft_i2c_bus_t *bus)
{
    uint8_t ack = 0;
    zino_pin_mode(bus->sda, ZINO_PIN_MODE_INPUT);
    zino_pin_mode(bus->scl, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_high(bus->scl);
    soft_i2c_delay_us(bus->delay_us);
    ack = zino_pin_read(bus->sda);
    zino_pin_low(bus->scl);
    soft_i2c_delay_us(bus->delay_us);
    return ack;
}
static uint8_t zino_soft_i2c_write(zino_soft_i2c_bus_t *bus, uint8_t regaddr, uint8_t *buff, uint8_t len)
{
    uint8_t error = 0;
    if (zino_soft_i2c_start(bus))
    {
        error = 1; // bus busy;
    }

    zino_soft_i2c_sendByte(bus, bus->slave_addr);
    if (zino_soft_i2c_readAck(bus))
    {
        error = 2; // slave not ack
    }

    zino_soft_i2c_sendByte(bus, regaddr);
    if (zino_soft_i2c_readAck(bus))
    {
        error = 3; // register not ack
    }

    for (uint8_t i = 0; i < len; i++)
    {
        zino_soft_i2c_sendByte(bus, buff[i]);
        if (zino_soft_i2c_readAck(bus))
        {
            error = 4; // data not ack
        }
    }
    zino_soft_i2c_stop(bus);
    return error;
}
static uint8_t zino_soft_i2c_read(zino_soft_i2c_bus_t *bus, uint8_t regaddr, uint8_t *buff, uint8_t len)
{
    uint8_t error = 0;
    if (zino_soft_i2c_start(bus))
    {
        error = 1; // bus busy;
    }
    zino_soft_i2c_sendByte(bus, bus->slave_addr);
    if (zino_soft_i2c_readAck(bus))
    {
        error = 2; // slave not ack
    }
    zino_soft_i2c_sendByte(bus, regaddr);
    if (zino_soft_i2c_readAck(bus))
    {
        error = 3; // register not ack
    }
    zino_soft_i2c_start(bus);
    zino_soft_i2c_sendByte(bus, bus->slave_addr | 0x01);
    if (zino_soft_i2c_readAck(bus))
    {
        error = 4; // slave not ack
    }
    for (uint8_t i = 0; i < len-1; i++)
    {
        buff[i] = zino_soft_i2c_readByte(bus);
        zino_soft_i2c_sendAck(bus,0);
    }
    buff[len-1] = zino_soft_i2c_readByte(bus);
    zino_soft_i2c_sendAck(bus,1);

    zino_soft_i2c_stop(bus);
    return error;
}
static uint8_t zino_soft_i2c_scan(zino_soft_i2c_bus_t *bus, uint8_t *buff)
{
    uint8_t devFound=0;

    for(uint8_t i = 0; i < 128; i++)
    {
        if(zino_soft_i2c_start(bus))
        {
            rt_kprintf("i2c busy");
        }
        zino_soft_i2c_sendByte(bus, i<<1);
        if(!zino_soft_i2c_readAck(bus))
        {
            devFound++;
            *buff = i<<1;
            buff++;
        }
        zino_soft_i2c_stop(bus);
    }
    return devFound;
}
static struct zino_soft_i2c_ops zino_soft_i2c_ops ={
    .start = zino_soft_i2c_start,
    .stop = zino_soft_i2c_stop,
    .sendByte = zino_soft_i2c_sendByte,
    .readByte = zino_soft_i2c_readByte,
    .readAck = zino_soft_i2c_readAck,
    .sendAck = zino_soft_i2c_sendAck,
    .scan = zino_soft_i2c_scan,
    .read = zino_soft_i2c_read,
    .write = zino_soft_i2c_write,
};
void zino_soft_i2c_init(zino_soft_i2c_bus_t *bus, zino_io_t *scl, zino_io_t *sda, uint8_t slave_addr, uint32_t delayUs)
{
    bus->scl = scl;
    bus->sda = sda;
    bus->slave_addr = slave_addr;
    bus->delay_us = delayUs;
    zino_pin_mode(sda, ZINO_PIN_MODE_OUTPUT_OD);
    zino_pin_mode(scl, ZINO_PIN_MODE_OUTPUT_OD);
    bus->ops = &zino_soft_i2c_ops;
}
static zino_soft_i2c_bus_t i2c1;
int zino_soft_i2c1_init()
{
    static zino_io_t i2c1_scl = {GPIOF,GPIO_Pins_6};
    static zino_io_t i2c1_sda = {GPIOF,GPIO_Pins_7};
    zino_soft_i2c_init(&i2c1, &i2c1_scl, &i2c1_sda, 0x00, 5);
    uint8_t buff[128];
    uint8_t ret = i2c1.ops->scan(&i2c1, buff);
    LOG_I("i2c1 scan found: %d\n",ret);
    for(int i=0;i<ret;i++)
    {
        rt_kprintf("\t -- 0x%x\n",buff[i]);
    }

    rt_kprintf("\n");
    return 0;

}
ZINO_APP_EXPORT(zino_soft_i2c1_init);
